#include <linux/module.h>	/* Needed by all modules */
#include <linux/kernel.h>	/* Needed for KERN_DEBUG */
#include <linux/init.h>		/* Needed for the macros */
#include <linux/seq_file.h>	/* Needed for seq-printf */
#include <linux/uaccess.h>	/* Needed for copy_from_user */
#include <linux/proc_fs.h>
#include <linux/slab.h>		/* Needed for kmalloc/kfree */ 
#include <linux/sched.h>

static struct proc_dir_entry *example_dir, *example_file;
static int len, temp;
static char *msg;

#define DRIVER_AUTHOR "Sachin Desai <sachinpdesai@hotmail.com>"
#define DRIVER_DESC   "Linux kernel module(lkm) test"

static int file_proc_write(struct file *filp, const char *buf, size_t count, loff_t *offp)
{
    copy_from_user(msg,buf,count);

    len=count;
    temp=len;

    return count;
}

static int file_proc_read(struct file *filp, char *buf, size_t count, loff_t *offp ) 
{
    if(count>temp)
        count=temp;

    temp=temp-count;

    copy_to_user(buf,msg, count);

    if(count==0)
        temp=len;
   
    return count;
}

static int file_proc_show(struct seq_file *m, void *v) {
  seq_printf(m, "Hello proc!\n");
  return 0;
}

static int file_proc_open(struct inode *inode, struct  file *file) {
  return single_open(file, file_proc_show, NULL);
}

static const struct file_operations file_proc_fops = {
    .owner = THIS_MODULE,
    .open = file_proc_open,
    .read = file_proc_read,
    .write = file_proc_write,
    .llseek = seq_lseek,
  .  release = single_release,
};

int create_proc_entry(void)
{
    /* create a directory */
    example_dir = proc_mkdir("example_dir", NULL);
    if(example_dir == NULL)
        return -ENOMEM;
        
    /* create a file */
    example_file = proc_create("example_file", 0666, example_dir, &file_proc_fops);
    if(example_file == NULL) {
        remove_proc_entry("example_dir", NULL);
        return -ENOMEM;
    }
    msg=kmalloc(GFP_KERNEL,10*sizeof(char));

    return (0);
}

int delete_proc_entry(void)
{
    remove_proc_entry("example_file", example_dir);
    remove_proc_entry("example_dir", NULL);

    return (0);
}

/* 
 * Get rid of taint message by declaring code as GPL. 
 */
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);

